home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkCanvText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  47.6 KB  |  1,612 lines

  1. /* 
  2.  * tkCanvText.c --
  3.  *
  4.  *    This file implements text items for canvas widgets.
  5.  *
  6.  * Copyright (c) 1991-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkCanvText.c 1.56 96/02/17 17:45:17
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include "tkInt.h"
  17. #include "tkCanvas.h"
  18. #include "tkPort.h"
  19.  
  20. /*
  21.  * One of the following structures is kept for each line of text
  22.  * in a text item.  It contains geometry and display information
  23.  * for that line.
  24.  */
  25.  
  26. typedef struct TextLine {
  27.     char *firstChar;        /* Pointer to the first character in this
  28.                  * line (in the "text" field of enclosing
  29.                  * text item). */
  30.     int numChars;        /* Number of characters displayed in this
  31.                  * line. */
  32.     int totalChars;        /* Total number of characters included as
  33.                  * part of this line (may include an extra
  34.                  * space character at the end that isn't
  35.                  * displayed). */
  36.     int x, y;            /* Origin at which to draw line on screen
  37.                  * (in integer pixel units, but in canvas
  38.                  * coordinates, not screen coordinates). */
  39.     int x1, y1;            /* Upper-left pixel that is part of text
  40.                  * line on screen (again, in integer canvas
  41.                  * pixel units). */
  42.     int x2, y2;            /* Lower-left pixel that is part of text
  43.                  * line on screen (again, in integer canvas
  44.                  * pixel units). */
  45. } TextLine;
  46.  
  47. /*
  48.  * The structure below defines the record for each text item.
  49.  */
  50.  
  51. typedef struct TextItem  {
  52.     Tk_Item header;        /* Generic stuff that's the same for all
  53.                  * types.  MUST BE FIRST IN STRUCTURE. */
  54.     Tk_CanvasTextInfo *textInfoPtr;
  55.                 /* Pointer to a structure containing
  56.                  * information about the selection and
  57.                  * insertion cursor.  The structure is owned
  58.                  * by (and shared with) the generic canvas
  59.                  * code. */
  60.     char *text;            /* Text for item (malloc-ed). */
  61.     int numChars;        /* Number of non-NULL characters in text. */
  62.     double x, y;        /* Positioning point for text. */
  63.     Tk_Anchor anchor;        /* Where to anchor text relative to (x,y). */
  64.     int width;            /* Width of lines for word-wrap, pixels.
  65.                  * Zero means no word-wrap. */
  66.     Tk_Justify justify;        /* Justification mode for text. */
  67.     int rightEdge;        /* Pixel just to right of right edge of
  68.                  * area of text item.  Used for selecting
  69.                  * up to end of line. */
  70.     XFontStruct *fontPtr;    /* Font for drawing text. */
  71.     XColor *color;        /* Color for text. */
  72.     Pixmap stipple;        /* Stipple bitmap for text, or None. */
  73.     GC gc;            /* Graphics context for drawing text. */
  74.     TextLine *linePtr;        /* Pointer to array of structures describing
  75.                  * individual lines of text item (malloc-ed). */
  76.     int numLines;        /* Number of structs at *linePtr. */
  77.     int insertPos;        /* Insertion cursor is displayed just to left
  78.                  * of character with this index. */
  79.     GC cursorOffGC;        /* If not None, this gives a graphics context
  80.                  * to use to draw the insertion cursor when
  81.                  * it's off.  Usedif the selection and
  82.                  * insertion cursor colors are the same.  */
  83.     GC selTextGC;        /* Graphics context for selected text. */
  84. } TextItem;
  85.  
  86. /*
  87.  * Information used for parsing configuration specs:
  88.  */
  89.  
  90. static Tk_CustomOption tagsOption = {Tk_CanvasTagsParseProc,
  91.     Tk_CanvasTagsPrintProc, (ClientData) NULL
  92. };
  93.  
  94. static Tk_ConfigSpec configSpecs[] = {
  95.     {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
  96.     "center", Tk_Offset(TextItem, anchor),
  97.     TK_CONFIG_DONT_SET_DEFAULT},
  98.     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
  99.     "black", Tk_Offset(TextItem, color), 0},
  100.     {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL,
  101.     "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*",
  102.     Tk_Offset(TextItem, fontPtr), 0},
  103.     {TK_CONFIG_JUSTIFY, "-justify", (char *) NULL, (char *) NULL,
  104.     "left", Tk_Offset(TextItem, justify),
  105.     TK_CONFIG_DONT_SET_DEFAULT},
  106.     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
  107.     (char *) NULL, Tk_Offset(TextItem, stipple), TK_CONFIG_NULL_OK},
  108.     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
  109.     (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
  110.     {TK_CONFIG_STRING, "-text", (char *) NULL, (char *) NULL,
  111.     "", Tk_Offset(TextItem, text), 0},
  112.     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
  113.     "0", Tk_Offset(TextItem, width), TK_CONFIG_DONT_SET_DEFAULT},
  114.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  115.     (char *) NULL, 0, 0}
  116. };
  117.  
  118. /*
  119.  * Prototypes for procedures defined in this file:
  120.  */
  121.  
  122. static void        ComputeTextBbox _ANSI_ARGS_((Tk_Canvas canvas,
  123.                 TextItem *textPtr));
  124. static int        ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,
  125.                 Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
  126.                 char **argv, int flags));
  127. static int        CreateText _ANSI_ARGS_((Tcl_Interp *interp,
  128.                 Tk_Canvas canvas, struct Tk_Item *itemPtr,
  129.                 int argc, char **argv));
  130. static void        DeleteText _ANSI_ARGS_((Tk_Canvas canvas,
  131.                 Tk_Item *itemPtr, Display *display));
  132. static void        DisplayText _ANSI_ARGS_((Tk_Canvas canvas,
  133.                 Tk_Item *itemPtr, Display *display, Drawable dst,
  134.                 int x, int y, int width, int height));
  135. static int        GetSelText _ANSI_ARGS_((Tk_Canvas canvas,
  136.                 Tk_Item *itemPtr, int offset, char *buffer,
  137.                 int maxBytes));
  138. static int        GetTextIndex _ANSI_ARGS_((Tcl_Interp *interp,
  139.                 Tk_Canvas canvas, Tk_Item *itemPtr,
  140.                 char *indexString, int *indexPtr));
  141. static void        LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  142.                 char *string, int numChars));
  143. static void        ScaleText _ANSI_ARGS_((Tk_Canvas canvas,
  144.                 Tk_Item *itemPtr, double originX, double originY,
  145.                 double scaleX, double scaleY));
  146. static void        SetTextCursor _ANSI_ARGS_((Tk_Canvas canvas,
  147.                 Tk_Item *itemPtr, int index));
  148. static int        TextCoords _ANSI_ARGS_((Tcl_Interp *interp,
  149.                 Tk_Canvas canvas, Tk_Item *itemPtr,
  150.                 int argc, char **argv));
  151. static void        TextDeleteChars _ANSI_ARGS_((Tk_Canvas canvas,
  152.                 Tk_Item *itemPtr, int first, int last));
  153. static void        TextInsert _ANSI_ARGS_((Tk_Canvas canvas,
  154.                 Tk_Item *itemPtr, int beforeThis, char *string));
  155. static int        TextToArea _ANSI_ARGS_((Tk_Canvas canvas,
  156.                 Tk_Item *itemPtr, double *rectPtr));
  157. static double        TextToPoint _ANSI_ARGS_((Tk_Canvas canvas,
  158.                 Tk_Item *itemPtr, double *pointPtr));
  159. static int        TextToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  160.                 Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
  161. static void        TranslateText _ANSI_ARGS_((Tk_Canvas canvas,
  162.                 Tk_Item *itemPtr, double deltaX, double deltaY));
  163.  
  164. /*
  165.  * The structures below defines the rectangle and oval item types
  166.  * by means of procedures that can be invoked by generic item code.
  167.  */
  168.  
  169. Tk_ItemType tkTextType = {
  170.     "text",                /* name */
  171.     sizeof(TextItem),            /* itemSize */
  172.     CreateText,                /* createProc */
  173.     configSpecs,            /* configSpecs */
  174.     ConfigureText,            /* configureProc */
  175.     TextCoords,                /* coordProc */
  176.     DeleteText,                /* deleteProc */
  177.     DisplayText,            /* displayProc */
  178.     0,                    /* alwaysRedraw */
  179.     TextToPoint,            /* pointProc */
  180.     TextToArea,                /* areaProc */
  181.     TextToPostscript,            /* postscriptProc */
  182.     ScaleText,                /* scaleProc */
  183.     TranslateText,            /* translateProc */
  184.     GetTextIndex,            /* indexProc */
  185.     SetTextCursor,            /* icursorProc */
  186.     GetSelText,                /* selectionProc */
  187.     TextInsert,                /* insertProc */
  188.     TextDeleteChars,            /* dTextProc */
  189.     (Tk_ItemType *) NULL        /* nextPtr */
  190. };
  191.  
  192. /*
  193.  *--------------------------------------------------------------
  194.  *
  195.  * CreateText --
  196.  *
  197.  *    This procedure is invoked to create a new text item
  198.  *    in a canvas.
  199.  *
  200.  * Results:
  201.  *    A standard Tcl return value.  If an error occurred in
  202.  *    creating the item then an error message is left in
  203.  *    interp->result;  in this case itemPtr is left uninitialized
  204.  *    so it can be safely freed by the caller.
  205.  *
  206.  * Side effects:
  207.  *    A new text item is created.
  208.  *
  209.  *--------------------------------------------------------------
  210.  */
  211.  
  212. static int
  213. CreateText(interp, canvas, itemPtr, argc, argv)
  214.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  215.     Tk_Canvas canvas;            /* Canvas to hold new item. */
  216.     Tk_Item *itemPtr;            /* Record to hold new item;  header
  217.                      * has been initialized by caller. */
  218.     int argc;                /* Number of arguments in argv. */
  219.     char **argv;            /* Arguments describing rectangle. */
  220. {
  221.     TextItem *textPtr = (TextItem *) itemPtr;
  222.  
  223.     if (argc < 2) {
  224.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  225.         Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
  226.         itemPtr->typePtr->name, " x y ?options?\"", (char *) NULL);
  227.     return TCL_ERROR;
  228.     }
  229.  
  230.     /*
  231.      * Carry out initialization that is needed in order to clean
  232.      * up after errors during the the remainder of this procedure.
  233.      */
  234.  
  235.     textPtr->text = NULL;
  236.     textPtr->textInfoPtr = Tk_CanvasGetTextInfo(canvas);
  237.     textPtr->numChars = 0;
  238.     textPtr->anchor = TK_ANCHOR_CENTER;
  239.     textPtr->width = 0;
  240.     textPtr->justify = TK_JUSTIFY_LEFT;
  241.     textPtr->rightEdge = 0;
  242.     textPtr->fontPtr = NULL;
  243.     textPtr->color = NULL;
  244.     textPtr->stipple = None;
  245.     textPtr->gc = None;
  246.     textPtr->linePtr = NULL;
  247.     textPtr->numLines = 0;
  248.     textPtr->insertPos = 0;
  249.     textPtr->cursorOffGC = None;
  250.     textPtr->selTextGC = None;
  251.  
  252.     /*
  253.      * Process the arguments to fill in the item record.
  254.      */
  255.  
  256.     if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &textPtr->x) != TCL_OK)
  257.         || (Tk_CanvasGetCoord(interp, canvas, argv[1], &textPtr->y)
  258.         != TCL_OK)) {
  259.     return TCL_ERROR;
  260.     }
  261.  
  262.     if (ConfigureText(interp, canvas, itemPtr, argc-2, argv+2, 0) != TCL_OK) {
  263.     DeleteText(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
  264.     return TCL_ERROR;
  265.     }
  266.     return TCL_OK;
  267. }
  268.  
  269. /*
  270.  *--------------------------------------------------------------
  271.  *
  272.  * TextCoords --
  273.  *
  274.  *    This procedure is invoked to process the "coords" widget
  275.  *    command on text items.  See the user documentation for
  276.  *    details on what it does.
  277.  *
  278.  * Results:
  279.  *    Returns TCL_OK or TCL_ERROR, and sets interp->result.
  280.  *
  281.  * Side effects:
  282.  *    The coordinates for the given item may be changed.
  283.  *
  284.  *--------------------------------------------------------------
  285.  */
  286.  
  287. static int
  288. TextCoords(interp, canvas, itemPtr, argc, argv)
  289.     Tcl_Interp *interp;            /* Used for error reporting. */
  290.     Tk_Canvas canvas;            /* Canvas containing item. */
  291.     Tk_Item *itemPtr;            /* Item whose coordinates are to be
  292.                      * read or modified. */
  293.     int argc;                /* Number of coordinates supplied in
  294.                      * argv. */
  295.     char **argv;            /* Array of coordinates: x1, y1,
  296.                      * x2, y2, ... */
  297. {
  298.     TextItem *textPtr = (TextItem *) itemPtr;
  299.     char x[TCL_DOUBLE_SPACE], y[TCL_DOUBLE_SPACE];
  300.  
  301.     if (argc == 0) {
  302.     Tcl_PrintDouble(interp, textPtr->x, x);
  303.     Tcl_PrintDouble(interp, textPtr->y, y);
  304.     Tcl_AppendResult(interp, x, " ", y, (char *) NULL);
  305.     } else if (argc == 2) {
  306.     if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &textPtr->x) != TCL_OK)
  307.         || (Tk_CanvasGetCoord(interp, canvas, argv[1],
  308.             &textPtr->y) != TCL_OK)) {
  309.         return TCL_ERROR;
  310.     }
  311.     ComputeTextBbox(canvas, textPtr);
  312.     } else {
  313.     sprintf(interp->result,
  314.         "wrong # coordinates: expected 0 or 2, got %d", argc);
  315.     return TCL_ERROR;
  316.     }
  317.     return TCL_OK;
  318. }
  319.  
  320. /*
  321.  *--------------------------------------------------------------
  322.  *
  323.  * ConfigureText --
  324.  *
  325.  *    This procedure is invoked to configure various aspects
  326.  *    of a text item, such as its border and background colors.
  327.  *
  328.  * Results:
  329.  *    A standard Tcl result code.  If an error occurs, then
  330.  *    an error message is left in interp->result.
  331.  *
  332.  * Side effects:
  333.  *    Configuration information, such as colors and stipple
  334.  *    patterns, may be set for itemPtr.
  335.  *
  336.  *--------------------------------------------------------------
  337.  */
  338.  
  339. static int
  340. ConfigureText(interp, canvas, itemPtr, argc, argv, flags)
  341.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  342.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  343.     Tk_Item *itemPtr;        /* Rectangle item to reconfigure. */
  344.     int argc;            /* Number of elements in argv.  */
  345.     char **argv;        /* Arguments describing things to configure. */
  346.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  347. {
  348.     TextItem *textPtr = (TextItem *) itemPtr;
  349.     XGCValues gcValues;
  350.     GC newGC, newSelGC;
  351.     unsigned long mask;
  352.     Tk_Window tkwin;
  353.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  354.     XColor *selBgColorPtr;
  355.  
  356.     tkwin = Tk_CanvasTkwin(canvas);
  357.     if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, argv,
  358.         (char *) textPtr, flags) != TCL_OK) {
  359.     return TCL_ERROR;
  360.     }
  361.  
  362.     /*
  363.      * A few of the options require additional processing, such as
  364.      * graphics contexts.
  365.      */
  366.  
  367.     textPtr->numChars = strlen(textPtr->text);
  368.     newGC = newSelGC = None;
  369.     if ((textPtr->color != NULL) && (textPtr->fontPtr != NULL)) {
  370.     gcValues.foreground = textPtr->color->pixel;
  371.     gcValues.font = textPtr->fontPtr->fid;
  372.     mask = GCForeground|GCFont;
  373.     if (textPtr->stipple != None) {
  374.         gcValues.stipple = textPtr->stipple;
  375.         gcValues.fill_style = FillStippled;
  376.         mask |= GCForeground|GCStipple|GCFillStyle;
  377.     }
  378.     newGC = Tk_GetGC(tkwin, mask, &gcValues);
  379.     gcValues.foreground = textInfoPtr->selFgColorPtr->pixel;
  380.     newSelGC = Tk_GetGC(tkwin, mask, &gcValues);
  381.     }
  382.     if (textPtr->gc != None) {
  383.     Tk_FreeGC(Tk_Display(tkwin), textPtr->gc);
  384.     }
  385.     textPtr->gc = newGC;
  386.     if (textPtr->selTextGC != None) {
  387.     Tk_FreeGC(Tk_Display(tkwin), textPtr->selTextGC);
  388.     }
  389.     textPtr->selTextGC = newSelGC;
  390.  
  391.     selBgColorPtr = Tk_3DBorderColor(textInfoPtr->selBorder);
  392.     if (Tk_3DBorderColor(textInfoPtr->insertBorder)->pixel
  393.         == selBgColorPtr->pixel) {
  394.     if (selBgColorPtr->pixel == BlackPixelOfScreen(Tk_Screen(tkwin))) {
  395.         gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
  396.     } else {
  397.         gcValues.foreground = BlackPixelOfScreen(Tk_Screen(tkwin));
  398.     }
  399.     newGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  400.     } else {
  401.     newGC = None;
  402.     }
  403.     if (textPtr->cursorOffGC != None) {
  404.     Tk_FreeGC(Tk_Display(tkwin), textPtr->cursorOffGC);
  405.     }
  406.     textPtr->cursorOffGC = newGC;
  407.  
  408.     /*
  409.      * If the text was changed, move the selection and insertion indices
  410.      * to keep them inside the item.
  411.      */
  412.  
  413.     if (textInfoPtr->selItemPtr == itemPtr) {
  414.     if (textInfoPtr->selectFirst >= textPtr->numChars) {
  415.         textInfoPtr->selItemPtr = NULL;
  416.     } else {
  417.         if (textInfoPtr->selectLast >= textPtr->numChars) {
  418.         textInfoPtr->selectLast = textPtr->numChars-1;
  419.         }
  420.         if ((textInfoPtr->anchorItemPtr == itemPtr)
  421.             && (textInfoPtr->selectAnchor >= textPtr->numChars)) {
  422.         textInfoPtr->selectAnchor = textPtr->numChars-1;
  423.         }
  424.     }
  425.     }
  426.     if (textPtr->insertPos >= textPtr->numChars) {
  427.     textPtr->insertPos = textPtr->numChars;
  428.     }
  429.  
  430.     ComputeTextBbox(canvas, textPtr);
  431.     return TCL_OK;
  432. }
  433.  
  434. /*
  435.  *--------------------------------------------------------------
  436.  *
  437.  * DeleteText --
  438.  *
  439.  *    This procedure is called to clean up the data structure
  440.  *    associated with a text item.
  441.  *
  442.  * Results:
  443.  *    None.
  444.  *
  445.  * Side effects:
  446.  *    Resources associated with itemPtr are released.
  447.  *
  448.  *--------------------------------------------------------------
  449.  */
  450.  
  451. static void
  452. DeleteText(canvas, itemPtr, display)
  453.     Tk_Canvas canvas;            /* Info about overall canvas widget. */
  454.     Tk_Item *itemPtr;            /* Item that is being deleted. */
  455.     Display *display;            /* Display containing window for
  456.                      * canvas. */
  457. {
  458.     TextItem *textPtr = (TextItem *) itemPtr;
  459.  
  460.     if (textPtr->text != NULL) {
  461.     ckfree(textPtr->text);
  462.     }
  463.     if (textPtr->fontPtr != NULL) {
  464.     Tk_FreeFontStruct(textPtr->fontPtr);
  465.     }
  466.     if (textPtr->color != NULL) {
  467.     Tk_FreeColor(textPtr->color);
  468.     }
  469.     if (textPtr->stipple != None) {
  470.     Tk_FreeBitmap(display, textPtr->stipple);
  471.     }
  472.     if (textPtr->gc != None) {
  473.     Tk_FreeGC(display, textPtr->gc);
  474.     }
  475.     if (textPtr->linePtr != NULL) {
  476.     ckfree((char *) textPtr->linePtr);
  477.     }
  478.     if (textPtr->cursorOffGC != None) {
  479.     Tk_FreeGC(display, textPtr->cursorOffGC);
  480.     }
  481.     if (textPtr->selTextGC != None) {
  482.     Tk_FreeGC(display, textPtr->selTextGC);
  483.     }
  484. }
  485.  
  486. /*
  487.  *--------------------------------------------------------------
  488.  *
  489.  * ComputeTextBbox --
  490.  *
  491.  *    This procedure is invoked to compute the bounding box of
  492.  *    all the pixels that may be drawn as part of a text item.
  493.  *    In addition, it recomputes all of the geometry information
  494.  *    used to display a text item or check for mouse hits.
  495.  *
  496.  * Results:
  497.  *    None.
  498.  *
  499.  * Side effects:
  500.  *    The fields x1, y1, x2, and y2 are updated in the header
  501.  *    for itemPtr, and the linePtr structure is regenerated
  502.  *    for itemPtr.
  503.  *
  504.  *--------------------------------------------------------------
  505.  */
  506.  
  507. static void
  508. ComputeTextBbox(canvas, textPtr)
  509.     Tk_Canvas canvas;            /* Canvas that contains item. */
  510.     TextItem *textPtr;            /* Item whose bbos is to be
  511.                      * recomputed. */
  512. {
  513.     TextLine *linePtr;
  514. #define MAX_LINES 100
  515.     char *lineStart[MAX_LINES];
  516.     int lineChars[MAX_LINES];
  517.     int linePixels[MAX_LINES];
  518.     int numLines, wrapPixels, maxLinePixels, leftX, topY, y;
  519.     int lineHeight, i, fudge;
  520.     char *p;
  521.     XCharStruct *maxBoundsPtr = &textPtr->fontPtr->max_bounds;
  522.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  523.  
  524.     if (textPtr->linePtr != NULL) {
  525.     ckfree((char *) textPtr->linePtr);
  526.     textPtr->linePtr = NULL;
  527.     }
  528.  
  529.     /*
  530.      * Work through the text computing the starting point, number of
  531.      * characters, and number of pixels in each line.
  532.      */
  533.  
  534.     p = textPtr->text;
  535.     maxLinePixels = 0;
  536.     if (textPtr->width > 0) {
  537.     wrapPixels = textPtr->width;
  538.     } else {
  539.     wrapPixels = 10000000;
  540.     }
  541.     for (numLines = 0; (numLines < MAX_LINES); numLines++) {
  542.     int numChars, numPixels;
  543.     numChars = TkMeasureChars(textPtr->fontPtr, p,
  544.         (textPtr->text + textPtr->numChars) - p, 0,
  545.         wrapPixels, 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &numPixels);
  546.     if (numPixels > maxLinePixels) {
  547.         maxLinePixels = numPixels;
  548.     }
  549.     lineStart[numLines] = p;
  550.     lineChars[numLines] = numChars;
  551.     linePixels[numLines] = numPixels;
  552.     p += numChars;
  553.  
  554.     /*
  555.      * Skip space character that terminates a line, if there is one.
  556.      * In the case of multiple spaces, all but one will be displayed.
  557.      * This is important to make sure the insertion cursor gets
  558.      * displayed when it is in the middle of a multi-space.
  559.      */
  560.  
  561.     if (isspace(UCHAR(*p))) {
  562.         p++;
  563.     } else if (*p == 0) {
  564.         /*
  565.          * The code below is tricky.  Putting the loop termination
  566.          * here guarantees that there's a TextLine for the last
  567.          * line of text, even if the line is empty (this can
  568.          * also happen if the entire text item is empty).  This is
  569.          * needed so that we can display the insertion cursor on a
  570.          * line even when it is empty.
  571.          */
  572.  
  573.         numLines++;
  574.         break;
  575.     }
  576.     }
  577.  
  578.     /*
  579.      * Use overall geometry information to compute the top-left corner
  580.      * of the bounding box for the text item.
  581.      */
  582.  
  583.     leftX = textPtr->x + 0.5;
  584.     topY = textPtr->y + 0.5;
  585.     lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent;
  586.     switch (textPtr->anchor) {
  587.     case TK_ANCHOR_NW:
  588.     case TK_ANCHOR_N:
  589.     case TK_ANCHOR_NE:
  590.         break;
  591.  
  592.     case TK_ANCHOR_W:
  593.     case TK_ANCHOR_CENTER:
  594.     case TK_ANCHOR_E:
  595.         topY -= (lineHeight * numLines)/2;
  596.         break;
  597.  
  598.     case TK_ANCHOR_SW:
  599.     case TK_ANCHOR_S:
  600.     case TK_ANCHOR_SE:
  601.         topY -= lineHeight * numLines;
  602.         break;
  603.     }
  604.     switch (textPtr->anchor) {
  605.     case TK_ANCHOR_NW:
  606.     case TK_ANCHOR_W:
  607.     case TK_ANCHOR_SW:
  608.         break;
  609.  
  610.     case TK_ANCHOR_N:
  611.     case TK_ANCHOR_CENTER:
  612.     case TK_ANCHOR_S:
  613.         leftX -= maxLinePixels/2;
  614.         break;
  615.  
  616.     case TK_ANCHOR_NE:
  617.     case TK_ANCHOR_E:
  618.     case TK_ANCHOR_SE:
  619.         leftX -= maxLinePixels;
  620.         break;
  621.     }
  622.     textPtr->rightEdge = leftX + maxLinePixels;
  623.  
  624.     /*
  625.      * Create the new TextLine array and fill it in using the geometry
  626.      * information gathered already.
  627.      */
  628.  
  629.     if (numLines > 0) {
  630.     textPtr->linePtr = (TextLine *) ckalloc((unsigned)
  631.         (numLines * sizeof(TextLine)));
  632.     } else {
  633.     textPtr->linePtr = NULL;
  634.     }
  635.     textPtr->numLines = numLines;
  636.     for (i = 0, linePtr = textPtr->linePtr, y = topY;
  637.         i < numLines; i++, linePtr++, y += lineHeight) {
  638.     linePtr->firstChar = lineStart[i];
  639.     linePtr->numChars = lineChars[i];
  640.     if (i == (numLines-1)) {
  641.         linePtr->totalChars = linePtr->numChars;
  642.     } else {
  643.         linePtr->totalChars = lineStart[i+1] - lineStart[i];
  644.     }
  645.     switch (textPtr->justify) {
  646.         case TK_JUSTIFY_LEFT:
  647.         linePtr->x = leftX;
  648.         break;
  649.         case TK_JUSTIFY_CENTER:
  650.         linePtr->x = leftX + maxLinePixels/2 - linePixels[i]/2;
  651.         break;
  652.         case TK_JUSTIFY_RIGHT:
  653.         linePtr->x = leftX + maxLinePixels - linePixels[i];
  654.         break;
  655.     }
  656.     linePtr->y = y + textPtr->fontPtr->ascent;
  657.     linePtr->x1 = linePtr->x + maxBoundsPtr->lbearing;
  658.     linePtr->y1 = y;
  659.     linePtr->x2 = linePtr->x + linePixels[i];
  660.     linePtr->y2 = linePtr->y + textPtr->fontPtr->descent - 1;
  661.     }
  662.  
  663.     /*
  664.      * Last of all, update the bounding box for the item.  The item's
  665.      * bounding box includes the bounding box of all its lines, plus
  666.      * an extra fudge factor for the cursor border (which could
  667.      * potentially be quite large).
  668.      */
  669.  
  670.     linePtr = textPtr->linePtr;
  671.     textPtr->header.x1 = textPtr->header.x2 = leftX;
  672.     textPtr->header.y1 = topY;
  673.     textPtr->header.y2 = topY + numLines*lineHeight;
  674.     for (linePtr = textPtr->linePtr, i = textPtr->numLines; i > 0;
  675.         i--, linePtr++) {
  676.     if (linePtr->x1 < textPtr->header.x1) {
  677.         textPtr->header.x1 = linePtr->x1;
  678.     }
  679.     if (linePtr->x2 >= textPtr->header.x2) {
  680.         textPtr->header.x2 = linePtr->x2 + 1;
  681.     }
  682.     }
  683.  
  684.     fudge = (textInfoPtr->insertWidth+1)/2;
  685.     if (textInfoPtr->selBorderWidth > fudge) {
  686.     fudge = textInfoPtr->selBorderWidth;
  687.     }
  688.     textPtr->header.x1 -= fudge;
  689.     textPtr->header.x2 += fudge;
  690. }
  691.  
  692. /*
  693.  *--------------------------------------------------------------
  694.  *
  695.  * DisplayText --
  696.  *
  697.  *    This procedure is invoked to draw a text item in a given
  698.  *    drawable.
  699.  *
  700.  * Results:
  701.  *    None.
  702.  *
  703.  * Side effects:
  704.  *    ItemPtr is drawn in drawable using the transformation
  705.  *    information in canvas.
  706.  *
  707.  *--------------------------------------------------------------
  708.  */
  709.  
  710. static void
  711. DisplayText(canvas, itemPtr, display, drawable, x, y, width, height)
  712.     Tk_Canvas canvas;            /* Canvas that contains item. */
  713.     Tk_Item *itemPtr;            /* Item to be displayed. */
  714.     Display *display;            /* Display on which to draw item. */
  715.     Drawable drawable;            /* Pixmap or window in which to draw
  716.                      * item. */
  717.     int x, y, width, height;        /* Describes region of canvas that
  718.                      * must be redisplayed (not used). */
  719. {
  720.     TextItem *textPtr = (TextItem *) itemPtr;
  721.     TextLine *linePtr;
  722.     int i, focusHere, insertX, insertIndex, lineIndex, tabOrigin;
  723.     int beforeSelect, inSelect, afterSelect, selStartX, selEndX;
  724.     short drawableX, drawableY;
  725.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  726.     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
  727.  
  728.     if (textPtr->gc == None) {
  729.     return;
  730.     }
  731.  
  732.     /*
  733.      * If we're stippling, then modify the stipple offset in the GC.  Be
  734.      * sure to reset the offset when done, since the GC is supposed to be
  735.      * read-only.
  736.      */
  737.  
  738.     if (textPtr->stipple != None) {
  739.     Tk_CanvasSetStippleOrigin(canvas, textPtr->gc);
  740.     }
  741.  
  742.     focusHere = (textInfoPtr->focusItemPtr == itemPtr) &&
  743.         (textInfoPtr->gotFocus);
  744.     for (linePtr = textPtr->linePtr, i = textPtr->numLines;
  745.         i > 0; linePtr++, i--) {
  746.  
  747.     /*
  748.      * If part or all of this line is selected, then draw a special
  749.      * background under the selected part of the line.
  750.      */
  751.  
  752.     lineIndex = linePtr->firstChar - textPtr->text;
  753.     if ((textInfoPtr->selItemPtr != itemPtr)
  754.         || (textInfoPtr->selectLast < lineIndex)
  755.         || (textInfoPtr->selectFirst >= (lineIndex
  756.             + linePtr->totalChars))) {
  757.         beforeSelect = linePtr->numChars;
  758.         inSelect = 0;
  759.     } else {
  760.         beforeSelect = textInfoPtr->selectFirst - lineIndex;
  761.         if (beforeSelect <= 0) {
  762.         beforeSelect = 0;
  763.         selStartX = linePtr->x;
  764.         } else {
  765.         (void) TkMeasureChars(textPtr->fontPtr,
  766.             linePtr->firstChar, beforeSelect, 0,
  767.             (int) 1000000, 0, TK_PARTIAL_OK, &selStartX);
  768.         selStartX += linePtr->x;
  769.         }
  770.         inSelect = textInfoPtr->selectLast + 1 - (lineIndex + beforeSelect);
  771.  
  772.         /*
  773.          * If the selection spans the end of this line, then display
  774.          * selection background all the way to the end of the line.
  775.          * However, for the last line we only want to display up to
  776.          * the last character, not the end of the line, hence the
  777.          * "i != 1" check.
  778.          */
  779.  
  780.         if (inSelect >= (linePtr->totalChars - beforeSelect)) {
  781.         inSelect = linePtr->numChars - beforeSelect;
  782.         if (i != 1) {
  783.             selEndX = textPtr->rightEdge;
  784.             goto fillSelectBackground;
  785.         }
  786.         }
  787.         (void) TkMeasureChars(textPtr->fontPtr,
  788.             linePtr->firstChar + beforeSelect, inSelect,
  789.             selStartX-linePtr->x, (int) 1000000, 0, TK_PARTIAL_OK,
  790.             &selEndX);
  791.         selEndX += linePtr->x;
  792.         fillSelectBackground:
  793.         Tk_CanvasDrawableCoords(canvas,
  794.             (double) (selStartX - textInfoPtr->selBorderWidth),
  795.             (double) (linePtr->y - textPtr->fontPtr->ascent),
  796.             &drawableX, &drawableY);
  797.         Tk_Fill3DRectangle(tkwin, drawable, textInfoPtr->selBorder,
  798.             drawableX, drawableY,
  799.             selEndX - selStartX + 2*textInfoPtr->selBorderWidth,
  800.             textPtr->fontPtr->ascent + textPtr->fontPtr->descent,
  801.             textInfoPtr->selBorderWidth, TK_RELIEF_RAISED);
  802.     }
  803.  
  804.     /*
  805.      * If the insertion cursor is in this line, then draw a special
  806.      * background for the cursor before drawing the text.  Note:
  807.      * if we're the cursor item but the cursor is turned off, then
  808.      * redraw background over the area of the cursor.  This guarantees
  809.      * that the selection won't make the cursor invisible on mono
  810.      * displays, where both are drawn in the same color.
  811.      */
  812.  
  813.     if (focusHere) {
  814.         insertIndex = textPtr->insertPos
  815.             - (linePtr->firstChar - textPtr->text);
  816.         if ((insertIndex >= 0) && (insertIndex <= linePtr->numChars)) {
  817.         (void) TkMeasureChars(textPtr->fontPtr, linePtr->firstChar,
  818.             insertIndex, 0, (int) 1000000, 0, TK_PARTIAL_OK, &insertX);
  819.         Tk_CanvasDrawableCoords(canvas,
  820.             (double) (linePtr->x + insertX
  821.                 - (textInfoPtr->insertWidth)/2),
  822.             (double) (linePtr->y - textPtr->fontPtr->ascent),
  823.             &drawableX, &drawableY);
  824.         if (textInfoPtr->cursorOn) {
  825.             Tk_Fill3DRectangle(tkwin, drawable,
  826.                 textInfoPtr->insertBorder, drawableX, drawableY,
  827.                 textInfoPtr->insertWidth,
  828.                 textPtr->fontPtr->ascent
  829.                 + textPtr->fontPtr->descent,
  830.                 textInfoPtr->insertBorderWidth, TK_RELIEF_RAISED);
  831.         } else if (textPtr->cursorOffGC != None) {
  832.             /* Redraw the background over the area of the cursor,
  833.              * even though the cursor is turned off.  This guarantees
  834.              * that the selection won't make the cursor invisible on
  835.              * mono displays, where both may be drawn in the same
  836.              * color.
  837.              */
  838.  
  839.             XFillRectangle(display, drawable, textPtr->cursorOffGC,
  840.                 drawableX, drawableY,
  841.                 (unsigned) textInfoPtr->insertWidth,
  842.                 (unsigned) (textPtr->fontPtr->ascent
  843.                 + textPtr->fontPtr->descent));
  844.         }
  845.         }
  846.     }
  847.  
  848.     /*
  849.      * Display the text in three pieces:  the part before the
  850.      * selection, the selected part (which needs a different graphics
  851.      * context), and the part after the selection.
  852.      */
  853.  
  854.     Tk_CanvasDrawableCoords(canvas, (double) linePtr->x,
  855.         (double) linePtr->y, &drawableX, &drawableY);
  856.     tabOrigin = drawableX;
  857.     if (beforeSelect != 0) {
  858.         TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr,
  859.             linePtr->firstChar, beforeSelect, drawableX,
  860.             drawableY, tabOrigin, 0);
  861.     }
  862.     if (inSelect != 0) {
  863.         Tk_CanvasDrawableCoords(canvas, (double) selStartX,
  864.             (double) linePtr->y, &drawableX, &drawableY);
  865.         TkDisplayChars(display, drawable, textPtr->selTextGC,
  866.             textPtr->fontPtr, linePtr->firstChar + beforeSelect,
  867.             inSelect, drawableX, drawableY, tabOrigin, 0);
  868.     }
  869.     afterSelect = linePtr->numChars - beforeSelect - inSelect;
  870.     if (afterSelect > 0) {
  871.         Tk_CanvasDrawableCoords(canvas, (double) selEndX,
  872.             (double) linePtr->y, &drawableX, &drawableY);
  873.         TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr,
  874.             linePtr->firstChar + beforeSelect + inSelect,
  875.             afterSelect, drawableX, drawableY, tabOrigin, 0);
  876.     }
  877.     }
  878.     if (textPtr->stipple != None) {
  879.     XSetTSOrigin(display, textPtr->gc, 0, 0);
  880.     }
  881. }
  882.  
  883. /*
  884.  *--------------------------------------------------------------
  885.  *
  886.  * TextInsert --
  887.  *
  888.  *    Insert characters into a text item at a given position.
  889.  *
  890.  * Results:
  891.  *    None.
  892.  *
  893.  * Side effects:
  894.  *    The text in the given item is modified.  The cursor and
  895.  *    selection positions are also modified to reflect the
  896.  *    insertion.
  897.  *
  898.  *--------------------------------------------------------------
  899.  */
  900.  
  901. static void
  902. TextInsert(canvas, itemPtr, beforeThis, string)
  903.     Tk_Canvas canvas;        /* Canvas containing text item. */
  904.     Tk_Item *itemPtr;        /* Text item to be modified. */
  905.     int beforeThis;        /* Index of character before which text is
  906.                  * to be inserted. */
  907.     char *string;        /* New characters to be inserted. */
  908. {
  909.     TextItem *textPtr = (TextItem *) itemPtr;
  910.     int length;
  911.     char *new;
  912.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  913.  
  914.     length = strlen(string);
  915.     if (length == 0) {
  916.     return;
  917.     }
  918.     if (beforeThis < 0) {
  919.     beforeThis = 0;
  920.     }
  921.     if (beforeThis > textPtr->numChars) {
  922.     beforeThis = textPtr->numChars;
  923.     }
  924.  
  925.     new = (char *) ckalloc((unsigned) (textPtr->numChars + length + 1));
  926.     strncpy(new, textPtr->text, (size_t) beforeThis);
  927.     strcpy(new+beforeThis, string);
  928.     strcpy(new+beforeThis+length, textPtr->text+beforeThis);
  929.     ckfree(textPtr->text);
  930.     textPtr->text = new;
  931.     textPtr->numChars += length;
  932.  
  933.     /*
  934.      * Inserting characters invalidates indices such as those for the
  935.      * selection and cursor.  Update the indices appropriately.
  936.      */
  937.  
  938.     if (textInfoPtr->selItemPtr == itemPtr) {
  939.     if (textInfoPtr->selectFirst >= beforeThis) {
  940.         textInfoPtr->selectFirst += length;
  941.     }
  942.     if (textInfoPtr->selectLast >= beforeThis) {
  943.         textInfoPtr->selectLast += length;
  944.     }
  945.     if ((textInfoPtr->anchorItemPtr == itemPtr)
  946.         && (textInfoPtr->selectAnchor >= beforeThis)) {
  947.         textInfoPtr->selectAnchor += length;
  948.     }
  949.     }
  950.     if (textPtr->insertPos >= beforeThis) {
  951.     textPtr->insertPos += length;
  952.     }
  953.     ComputeTextBbox(canvas, textPtr);
  954. }
  955.  
  956. /*
  957.  *--------------------------------------------------------------
  958.  *
  959.  * TextDeleteChars --
  960.  *
  961.  *    Delete one or more characters from a text item.
  962.  *
  963.  * Results:
  964.  *    None.
  965.  *
  966.  * Side effects:
  967.  *    Characters between "first" and "last", inclusive, get
  968.  *    deleted from itemPtr, and things like the selection
  969.  *    position get updated.
  970.  *
  971.  *--------------------------------------------------------------
  972.  */
  973.  
  974. static void
  975. TextDeleteChars(canvas, itemPtr, first, last)
  976.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  977.     Tk_Item *itemPtr;        /* Item in which to delete characters. */
  978.     int first;            /* Index of first character to delete. */
  979.     int last;            /* Index of last character to delete. */
  980. {
  981.     TextItem *textPtr = (TextItem *) itemPtr;
  982.     int count;
  983.     char *new;
  984.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  985.  
  986.     if (first < 0) {
  987.     first = 0;
  988.     }
  989.     if (last >= textPtr->numChars) {
  990.     last = textPtr->numChars-1;
  991.     }
  992.     if (first > last) {
  993.     return;
  994.     }
  995.     count = last + 1 - first;
  996.  
  997.     new = (char *) ckalloc((unsigned) (textPtr->numChars + 1 - count));
  998.     strncpy(new, textPtr->text, (size_t) first);
  999.     strcpy(new+first, textPtr->text+last+1);
  1000.     ckfree(textPtr->text);
  1001.     textPtr->text = new;
  1002.     textPtr->numChars -= count;
  1003.  
  1004.     /*
  1005.      * Update indexes for the selection and cursor to reflect the
  1006.      * renumbering of the remaining characters.
  1007.      */
  1008.  
  1009.     if (textInfoPtr->selItemPtr == itemPtr) {
  1010.     if (textInfoPtr->selectFirst > first) {
  1011.         textInfoPtr->selectFirst -= count;
  1012.         if (textInfoPtr->selectFirst < first) {
  1013.         textInfoPtr->selectFirst = first;
  1014.         }
  1015.     }
  1016.     if (textInfoPtr->selectLast >= first) {
  1017.         textInfoPtr->selectLast -= count;
  1018.         if (textInfoPtr->selectLast < (first-1)) {
  1019.         textInfoPtr->selectLast = (first-1);
  1020.         }
  1021.     }
  1022.     if (textInfoPtr->selectFirst > textInfoPtr->selectLast) {
  1023.         textInfoPtr->selItemPtr = NULL;
  1024.     }
  1025.     if ((textInfoPtr->anchorItemPtr == itemPtr)
  1026.         && (textInfoPtr->selectAnchor > first)) {
  1027.         textInfoPtr->selectAnchor -= count;
  1028.         if (textInfoPtr->selectAnchor < first) {
  1029.         textInfoPtr->selectAnchor = first;
  1030.         }
  1031.     }
  1032.     }
  1033.     if (textPtr->insertPos > first) {
  1034.     textPtr->insertPos -= count;
  1035.     if (textPtr->insertPos < first) {
  1036.         textPtr->insertPos = first;
  1037.     }
  1038.     }
  1039.     ComputeTextBbox(canvas, textPtr);
  1040.     return;
  1041. }
  1042.  
  1043. /*
  1044.  *--------------------------------------------------------------
  1045.  *
  1046.  * TextToPoint --
  1047.  *
  1048.  *    Computes the distance from a given point to a given
  1049.  *    text item, in canvas units.
  1050.  *
  1051.  * Results:
  1052.  *    The return value is 0 if the point whose x and y coordinates
  1053.  *    are pointPtr[0] and pointPtr[1] is inside the arc.  If the
  1054.  *    point isn't inside the arc then the return value is the
  1055.  *    distance from the point to the arc.
  1056.  *
  1057.  * Side effects:
  1058.  *    None.
  1059.  *
  1060.  *--------------------------------------------------------------
  1061.  */
  1062.  
  1063. static double
  1064. TextToPoint(canvas, itemPtr, pointPtr)
  1065.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  1066.     Tk_Item *itemPtr;        /* Item to check against point. */
  1067.     double *pointPtr;        /* Pointer to x and y coordinates. */
  1068. {
  1069.     TextItem *textPtr = (TextItem *) itemPtr;
  1070.     TextLine *linePtr;
  1071.     int i;
  1072.     double xDiff, yDiff, dist, minDist;
  1073.  
  1074.     /*
  1075.      * Treat each line in the text item as a rectangle, compute the
  1076.      * distance to that rectangle, and take the minimum of these
  1077.      * distances.  Perform most of the calculations in integer pixel
  1078.      * units, since that's how the dimensions of the text are defined.
  1079.      */
  1080.  
  1081.     minDist = -1.0;
  1082.     for (linePtr = textPtr->linePtr, i = textPtr->numLines;
  1083.         i > 0; linePtr++, i--) {
  1084.  
  1085.     /*
  1086.      * If the point is inside the line's rectangle, then can
  1087.      * return immediately.
  1088.      */
  1089.     
  1090.     if ((pointPtr[0] >= linePtr->x1)
  1091.         && (pointPtr[0] <= linePtr->x2)
  1092.         && (pointPtr[1] >= linePtr->y1)
  1093.         && (pointPtr[1] <= linePtr->y2)) {
  1094.         return 0.0;
  1095.     }
  1096.     
  1097.     /*
  1098.      * Point is outside line's rectangle; compute distance to nearest
  1099.      * side.
  1100.      */
  1101.     
  1102.     if (pointPtr[0] < linePtr->x1) {
  1103.         xDiff = linePtr->x1 - pointPtr[0];
  1104.     } else if (pointPtr[0] > linePtr->x2)  {
  1105.         xDiff = pointPtr[0] - linePtr->x2;
  1106.     } else {
  1107.         xDiff = 0;
  1108.     }
  1109.     
  1110.     if (pointPtr[1] < linePtr->y1) {
  1111.         yDiff = linePtr->y1 - pointPtr[1];
  1112.     } else if (pointPtr[1] > linePtr->y2)  {
  1113.         yDiff = pointPtr[1] - linePtr->y2;
  1114.     } else {
  1115.         yDiff = 0;
  1116.     }
  1117.  
  1118.     dist = hypot(xDiff, yDiff);
  1119.     if ((dist < minDist) || (minDist < 0.0)) {
  1120.         minDist = dist;
  1121.     }
  1122.     }
  1123.     return minDist;
  1124. }
  1125.  
  1126. /*
  1127.  *--------------------------------------------------------------
  1128.  *
  1129.  * TextToArea --
  1130.  *
  1131.  *    This procedure is called to determine whether an item
  1132.  *    lies entirely inside, entirely outside, or overlapping
  1133.  *    a given rectangle.
  1134.  *
  1135.  * Results:
  1136.  *    -1 is returned if the item is entirely outside the area
  1137.  *    given by rectPtr, 0 if it overlaps, and 1 if it is entirely
  1138.  *    inside the given area.
  1139.  *
  1140.  * Side effects:
  1141.  *    None.
  1142.  *
  1143.  *--------------------------------------------------------------
  1144.  */
  1145.  
  1146. static int
  1147. TextToArea(canvas, itemPtr, rectPtr)
  1148.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  1149.     Tk_Item *itemPtr;        /* Item to check against rectangle. */
  1150.     double *rectPtr;        /* Pointer to array of four coordinates
  1151.                  * (x1, y1, x2, y2) describing rectangular
  1152.                  * area.  */
  1153. {
  1154.     TextItem *textPtr = (TextItem *) itemPtr;
  1155.     TextLine *linePtr;
  1156.     int i, result;
  1157.  
  1158.     /*
  1159.      * Scan the lines one at a time, seeing whether each line is
  1160.      * entirely in, entirely out, or overlapping the rectangle.  If
  1161.      * an overlap is detected, return immediately;  otherwise wait
  1162.      * until all lines have been processed and see if they were all
  1163.      * inside or all outside.
  1164.      */
  1165.  
  1166.     result = 0;
  1167.     for (linePtr = textPtr->linePtr, i = textPtr->numLines;
  1168.         i > 0; linePtr++, i--) {
  1169.     if ((rectPtr[2] < linePtr->x1) || (rectPtr[0] > linePtr->x2)
  1170.         || (rectPtr[3] < linePtr->y1) || (rectPtr[1] > linePtr->y2)) {
  1171.         if (result == 1) {
  1172.         return 0;
  1173.         }
  1174.         result = -1;
  1175.         continue;
  1176.     }
  1177.     if ((linePtr->x1 < rectPtr[0]) || (linePtr->x2 > rectPtr[2])
  1178.         || (linePtr->y1 < rectPtr[1]) || (linePtr->y2 > rectPtr[3])) {
  1179.         return 0;
  1180.     }
  1181.     if (result == -1) {
  1182.         return 0;
  1183.     }
  1184.     result = 1;
  1185.     }
  1186.     return result;
  1187. }
  1188.  
  1189. /*
  1190.  *--------------------------------------------------------------
  1191.  *
  1192.  * ScaleText --
  1193.  *
  1194.  *    This procedure is invoked to rescale a text item.
  1195.  *
  1196.  * Results:
  1197.  *    None.
  1198.  *
  1199.  * Side effects:
  1200.  *    Scales the position of the text, but not the size
  1201.  *    of the font for the text.
  1202.  *
  1203.  *--------------------------------------------------------------
  1204.  */
  1205.  
  1206.     /* ARGSUSED */
  1207. static void
  1208. ScaleText(canvas, itemPtr, originX, originY, scaleX, scaleY)
  1209.     Tk_Canvas canvas;            /* Canvas containing rectangle. */
  1210.     Tk_Item *itemPtr;            /* Rectangle to be scaled. */
  1211.     double originX, originY;        /* Origin about which to scale rect. */
  1212.     double scaleX;            /* Amount to scale in X direction. */
  1213.     double scaleY;            /* Amount to scale in Y direction. */
  1214. {
  1215.     TextItem *textPtr = (TextItem *) itemPtr;
  1216.  
  1217.     textPtr->x = originX + scaleX*(textPtr->x - originX);
  1218.     textPtr->y = originY + scaleY*(textPtr->y - originY);
  1219.     ComputeTextBbox(canvas, textPtr);
  1220.     return;
  1221. }
  1222.  
  1223. /*
  1224.  *--------------------------------------------------------------
  1225.  *
  1226.  * TranslateText --
  1227.  *
  1228.  *    This procedure is called to move a text item by a
  1229.  *    given amount.
  1230.  *
  1231.  * Results:
  1232.  *    None.
  1233.  *
  1234.  * Side effects:
  1235.  *    The position of the text item is offset by (xDelta, yDelta),
  1236.  *    and the bounding box is updated in the generic part of the
  1237.  *    item structure.
  1238.  *
  1239.  *--------------------------------------------------------------
  1240.  */
  1241.  
  1242. static void
  1243. TranslateText(canvas, itemPtr, deltaX, deltaY)
  1244.     Tk_Canvas canvas;            /* Canvas containing item. */
  1245.     Tk_Item *itemPtr;            /* Item that is being moved. */
  1246.     double deltaX, deltaY;        /* Amount by which item is to be
  1247.                      * moved. */
  1248. {
  1249.     TextItem *textPtr = (TextItem *) itemPtr;
  1250.  
  1251.     textPtr->x += deltaX;
  1252.     textPtr->y += deltaY;
  1253.     ComputeTextBbox(canvas, textPtr);
  1254. }
  1255.  
  1256. /*
  1257.  *--------------------------------------------------------------
  1258.  *
  1259.  * GetTextIndex --
  1260.  *
  1261.  *    Parse an index into a text item and return either its value
  1262.  *    or an error.
  1263.  *
  1264.  * Results:
  1265.  *    A standard Tcl result.  If all went well, then *indexPtr is
  1266.  *    filled in with the index (into itemPtr) corresponding to
  1267.  *    string.  Otherwise an error message is left in
  1268.  *    interp->result.
  1269.  *
  1270.  * Side effects:
  1271.  *    None.
  1272.  *
  1273.  *--------------------------------------------------------------
  1274.  */
  1275.  
  1276. static int
  1277. GetTextIndex(interp, canvas, itemPtr, string, indexPtr)
  1278.     Tcl_Interp *interp;        /* Used for error reporting. */
  1279.     Tk_Canvas canvas;        /* Canvas containing item. */
  1280.     Tk_Item *itemPtr;        /* Item for which the index is being
  1281.                  * specified. */
  1282.     char *string;        /* Specification of a particular character
  1283.                  * in itemPtr's text. */
  1284.     int *indexPtr;        /* Where to store converted index. */
  1285. {
  1286.     TextItem *textPtr = (TextItem *) itemPtr;
  1287.     size_t length;
  1288.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  1289.  
  1290.     length = strlen(string);
  1291.  
  1292.     if (string[0] == 'e') {
  1293.     if (strncmp(string, "end", length) == 0) {
  1294.         *indexPtr = textPtr->numChars;
  1295.     } else {
  1296.         badIndex:
  1297.  
  1298.         /*
  1299.          * Some of the paths here leave messages in interp->result,
  1300.          * so we have to clear it out before storing our own message.
  1301.          */
  1302.  
  1303.         Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
  1304.         Tcl_AppendResult(interp, "bad index \"", string, "\"",
  1305.             (char *) NULL);
  1306.         return TCL_ERROR;
  1307.     }
  1308.     } else if (string[0] == 'i') {
  1309.     if (strncmp(string, "insert", length) == 0) {
  1310.         *indexPtr = textPtr->insertPos;
  1311.     } else {
  1312.         goto badIndex;
  1313.     }
  1314.     } else if (string[0] == 's') {
  1315.     if (textInfoPtr->selItemPtr != itemPtr) {
  1316.         interp->result = "selection isn't in item";
  1317.         return TCL_ERROR;
  1318.     }
  1319.     if (length < 5) {
  1320.         goto badIndex;
  1321.     }
  1322.     if (strncmp(string, "sel.first", length) == 0) {
  1323.         *indexPtr = textInfoPtr->selectFirst;
  1324.     } else if (strncmp(string, "sel.last", length) == 0) {
  1325.         *indexPtr = textInfoPtr->selectLast;
  1326.     } else {
  1327.         goto badIndex;
  1328.     }
  1329.     } else if (string[0] == '@') {
  1330.     int x, y, dummy, i;
  1331.     double tmp;
  1332.     char *end, *p;
  1333.     TextLine *linePtr;
  1334.  
  1335.     p = string+1;
  1336.     tmp = strtod(p, &end);
  1337.     if ((end == p) || (*end != ',')) {
  1338.         goto badIndex;
  1339.     }
  1340.     x = (tmp < 0) ? tmp - 0.5 : tmp + 0.5;
  1341.     p = end+1;
  1342.     tmp = strtod(p, &end);
  1343.     if ((end == p) || (*end != 0)) {
  1344.         goto badIndex;
  1345.     }
  1346.     y = (tmp < 0) ? tmp - 0.5 : tmp + 0.5;
  1347.     if ((textPtr->numChars == 0) || (y < textPtr->linePtr[0].y1)) {
  1348.         *indexPtr = 0;
  1349.         return TCL_OK;
  1350.     }
  1351.     for (i = 0, linePtr = textPtr->linePtr; ; i++, linePtr++) {
  1352.         if (i >= textPtr->numLines) {
  1353.         *indexPtr = textPtr->numChars;
  1354.         return TCL_OK;
  1355.         }
  1356.         if (y <= linePtr->y2) {
  1357.         break;
  1358.         }
  1359.     }
  1360.     *indexPtr = TkMeasureChars(textPtr->fontPtr, linePtr->firstChar,
  1361.         linePtr->numChars, linePtr->x, x, linePtr->x, 0, &dummy);
  1362.     *indexPtr += linePtr->firstChar - textPtr->text;
  1363.     } else {
  1364.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  1365.         goto badIndex;
  1366.     }
  1367.     if (*indexPtr < 0){
  1368.         *indexPtr = 0;
  1369.     } else if (*indexPtr > textPtr->numChars) {
  1370.         *indexPtr = textPtr->numChars;
  1371.     }
  1372.     }
  1373.     return TCL_OK;
  1374. }
  1375.  
  1376. /*
  1377.  *--------------------------------------------------------------
  1378.  *
  1379.  * SetTextCursor --
  1380.  *
  1381.  *    Set the position of the insertion cursor in this item.
  1382.  *
  1383.  * Results:
  1384.  *    None.
  1385.  *
  1386.  * Side effects:
  1387.  *    The cursor position will change.
  1388.  *
  1389.  *--------------------------------------------------------------
  1390.  */
  1391.  
  1392.     /* ARGSUSED */
  1393. static void
  1394. SetTextCursor(canvas, itemPtr, index)
  1395.     Tk_Canvas canvas;            /* Record describing canvas widget. */
  1396.     Tk_Item *itemPtr;            /* Text item in which cursor position
  1397.                      * is to be set. */
  1398.     int index;                /* Index of character just before which
  1399.                      * cursor is to be positioned. */
  1400. {
  1401.     TextItem *textPtr = (TextItem *) itemPtr;
  1402.  
  1403.     if (index < 0) {
  1404.     textPtr->insertPos = 0;
  1405.     } else  if (index > textPtr->numChars) {
  1406.     textPtr->insertPos = textPtr->numChars;
  1407.     } else {
  1408.     textPtr->insertPos = index;
  1409.     }
  1410. }
  1411.  
  1412. /*
  1413.  *--------------------------------------------------------------
  1414.  *
  1415.  * GetSelText --
  1416.  *
  1417.  *    This procedure is invoked to return the selected portion
  1418.  *    of a text item.  It is only called when this item has
  1419.  *    the selection.
  1420.  *
  1421.  * Results:
  1422.  *    The return value is the number of non-NULL bytes stored
  1423.  *    at buffer.  Buffer is filled (or partially filled) with a
  1424.  *    NULL-terminated string containing part or all of the selection,
  1425.  *    as given by offset and maxBytes.
  1426.  *
  1427.  * Side effects:
  1428.  *    None.
  1429.  *
  1430.  *--------------------------------------------------------------
  1431.  */
  1432.  
  1433. static int
  1434. GetSelText(canvas, itemPtr, offset, buffer, maxBytes)
  1435.     Tk_Canvas canvas;            /* Canvas containing selection. */
  1436.     Tk_Item *itemPtr;            /* Text item containing selection. */
  1437.     int offset;                /* Offset within selection of first
  1438.                      * character to be returned. */
  1439.     char *buffer;            /* Location in which to place
  1440.                      * selection. */
  1441.     int maxBytes;            /* Maximum number of bytes to place
  1442.                      * at buffer, not including terminating
  1443.                      * NULL character. */
  1444. {
  1445.     TextItem *textPtr = (TextItem *) itemPtr;
  1446.     int count;
  1447.     Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
  1448.  
  1449.     count = textInfoPtr->selectLast + 1 - textInfoPtr->selectFirst - offset;
  1450.     if (textInfoPtr->selectLast == textPtr->numChars) {
  1451.     count -= 1;
  1452.     }
  1453.     if (count > maxBytes) {
  1454.     count = maxBytes;
  1455.     }
  1456.     if (count <= 0) {
  1457.     return 0;
  1458.     }
  1459.     strncpy(buffer, textPtr->text + textInfoPtr->selectFirst + offset,
  1460.         (size_t) count);
  1461.     buffer[count] = '\0';
  1462.     return count;
  1463. }
  1464.  
  1465. /*
  1466.  *--------------------------------------------------------------
  1467.  *
  1468.  * TextToPostscript --
  1469.  *
  1470.  *    This procedure is called to generate Postscript for
  1471.  *    text items.
  1472.  *
  1473.  * Results:
  1474.  *    The return value is a standard Tcl result.  If an error
  1475.  *    occurs in generating Postscript then an error message is
  1476.  *    left in interp->result, replacing whatever used
  1477.  *    to be there.  If no error occurs, then Postscript for the
  1478.  *    item is appended to the result.
  1479.  *
  1480.  * Side effects:
  1481.  *    None.
  1482.  *
  1483.  *--------------------------------------------------------------
  1484.  */
  1485.  
  1486. static int
  1487. TextToPostscript(interp, canvas, itemPtr, prepass)
  1488.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1489.                      * here. */
  1490.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1491.     Tk_Item *itemPtr;            /* Item for which Postscript is
  1492.                      * wanted. */
  1493.     int prepass;            /* 1 means this is a prepass to
  1494.                      * collect font information;  0 means
  1495.                      * final Postscript is being created. */
  1496. {
  1497.     TextItem *textPtr = (TextItem *) itemPtr;
  1498.     TextLine *linePtr;
  1499.     int i;
  1500.     char *xoffset = NULL, *yoffset = NULL;    /* Initializations needed */
  1501.     char *justify = NULL;            /* only to stop compiler
  1502.                             * warnings. */
  1503.     char buffer[500];
  1504.  
  1505.     if (textPtr->color == NULL) {
  1506.     return TCL_OK;
  1507.     }
  1508.  
  1509.     if (Tk_CanvasPsFont(interp, canvas, textPtr->fontPtr) != TCL_OK) {
  1510.     return TCL_ERROR;
  1511.     }
  1512.     if (Tk_CanvasPsColor(interp, canvas, textPtr->color) != TCL_OK) {
  1513.     return TCL_ERROR;
  1514.     }
  1515.     if (textPtr->stipple != None) {
  1516.     Tcl_AppendResult(interp, "/StippleText {\n    ",
  1517.         (char *) NULL);
  1518.     Tk_CanvasPsStipple(interp, canvas, textPtr->stipple);
  1519.     Tcl_AppendResult(interp, "} bind def\n", (char *) NULL);
  1520.     }
  1521.     sprintf(buffer, "%.15g %.15g [\n", textPtr->x,
  1522.         Tk_CanvasPsY(canvas, textPtr->y));
  1523.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1524.     for (i = textPtr->numLines, linePtr = textPtr->linePtr;
  1525.         i > 0; i--, linePtr++) {
  1526.     Tcl_AppendResult(interp, "    ", (char *) NULL);
  1527.     LineToPostscript(interp, linePtr->firstChar,
  1528.         linePtr->numChars);
  1529.     Tcl_AppendResult(interp, "\n", (char *) NULL);
  1530.     }
  1531.     switch (textPtr->anchor) {
  1532.     case TK_ANCHOR_NW:     xoffset = "0";    yoffset = "0";   break;
  1533.     case TK_ANCHOR_N:      xoffset = "-0.5"; yoffset = "0";   break;
  1534.     case TK_ANCHOR_NE:     xoffset = "-1";   yoffset = "0";   break;
  1535.     case TK_ANCHOR_E:      xoffset = "-1";   yoffset = "0.5"; break;
  1536.     case TK_ANCHOR_SE:     xoffset = "-1";   yoffset = "1";   break;
  1537.     case TK_ANCHOR_S:      xoffset = "-0.5"; yoffset = "1";   break;
  1538.     case TK_ANCHOR_SW:     xoffset = "0";    yoffset = "1";   break;
  1539.     case TK_ANCHOR_W:      xoffset = "0";    yoffset = "0.5"; break;
  1540.     case TK_ANCHOR_CENTER: xoffset = "-0.5"; yoffset = "0.5"; break;
  1541.     }
  1542.     switch (textPtr->justify) {
  1543.     case TK_JUSTIFY_LEFT:    justify = "0";   break;
  1544.     case TK_JUSTIFY_CENTER:    justify = "0.5"; break;
  1545.     case TK_JUSTIFY_RIGHT:    justify = "1";   break;
  1546.     }
  1547.     sprintf(buffer, "] %d %s %s %s %s DrawText\n",
  1548.         textPtr->fontPtr->ascent + textPtr->fontPtr->descent,
  1549.         xoffset, yoffset, justify,
  1550.         (textPtr->stipple == None) ? "false" : "true");
  1551.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1552.     return TCL_OK;
  1553. }
  1554.  
  1555. /*
  1556.  *--------------------------------------------------------------
  1557.  *
  1558.  * LineToPostscript --
  1559.  *
  1560.  *    This procedure generates a parenthesized Postscript string
  1561.  *    describing one line of text from a text item.
  1562.  *
  1563.  * Results:
  1564.  *    None. The parenthesized string is appended to
  1565.  *    interp->result.  It generates proper backslash notation so
  1566.  *    that Postscript can interpret the string correctly.
  1567.  *
  1568.  * Side effects:
  1569.  *    None.
  1570.  *
  1571.  *--------------------------------------------------------------
  1572.  */
  1573.  
  1574. static void
  1575. LineToPostscript(interp, string, numChars)
  1576.     Tcl_Interp *interp;        /* Interp whose result is to be appended to. */
  1577.     char *string;        /* String to Postscript-ify. */
  1578.     int numChars;        /* Number of characters in the string. */
  1579. {
  1580. #define BUFFER_SIZE 100
  1581.     char buffer[BUFFER_SIZE+5];
  1582.     int used, c;
  1583.  
  1584.     buffer[0] = '(';
  1585.     used = 1;
  1586.     for ( ; numChars > 0; string++, numChars--) {
  1587.     c = (*string) & 0xff;
  1588.     if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)
  1589.         || (c >= 0x7f)) {
  1590.         /*
  1591.          * Tricky point:  the "03" is necessary in the sprintf below,
  1592.          * so that a full three digits of octal are always generated.
  1593.          * Without the "03", a number following this sequence could
  1594.          * be interpreted by Postscript as part of this sequence.
  1595.          */
  1596.         sprintf(buffer+used, "\\%03o", c);
  1597.         used += strlen(buffer+used);
  1598.     } else {
  1599.         buffer[used] = c;
  1600.         used++;
  1601.     }
  1602.     if (used >= BUFFER_SIZE) {
  1603.         buffer[used] = 0;
  1604.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  1605.         used = 0;
  1606.     }
  1607.     }
  1608.     buffer[used] = ')';
  1609.     buffer[used+1] = 0;
  1610.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1611. }
  1612.